// SPDX-License-Identifier: BSD-3-Clause // // Copyright(c) 2018 Intel Corporation. All rights reserved. // // Author: Bartosz Kokoszko // Artur Kloniecki #include #include #include #include #include #include #include #include #include #include #include "convert.h" #include "misc.h" #define APP_NAME "sof-logger" #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #endif static const char *debugfs[] = { "dmac0", "dmac1", "ssp0", "ssp1", "ssp2", "iram", "dram", "shim", "mbox", "etrace", "hda", "pp", "dsp", }; /** See PLATFORM_DEFAULT_CLOCK in the firmware code. */ static const float DEFAULT_CLOCK_MHZ = 19.2; static void usage(void) { fprintf(stdout, "Usage %s \n", APP_NAME); fprintf(stdout, "%s:\t \t\t\tDisplay mailbox or DMA trace contents\n", APP_NAME); fprintf(stdout, "%s:\t -i infile -o outfile\tDump infile contents " "to outfile\n", APP_NAME); fprintf(stdout, "%s:\t -l *.ldc_file\t\t.ldc input file generated by smex\n", APP_NAME); fprintf(stdout, "%s:\t -p \t\t\tInput from stdin\n", APP_NAME); fprintf(stdout, "%s:\t -v ver_file\t\tUse ver_file instead of " "/sys/kernel/debug/sof/fw_version\n", APP_NAME); fprintf(stdout, "%s:\t -n\t\t\tDo not compare dictionary checksums\n", APP_NAME); fprintf(stdout, "%s:\t -c clock\t\tSet timestamp clock in MHz, %.2f MHz by default\n", APP_NAME, DEFAULT_CLOCK_MHZ); fprintf(stdout, "%s:\t -s state_name\t\tTake a snapshot of state\n", APP_NAME); fprintf(stdout, "%s:\t -t\t\t\tDMA 'trace' stream instead of 'etrace' error mailbox\n", APP_NAME); fprintf(stdout, "%s:\t -u baud\t\tInput data from a UART\n", APP_NAME); fprintf(stdout, "%s:\t -r\t\t\tLess formatted output for " "chained log processors\n", APP_NAME); fprintf(stdout, "%s:\t -L\t\t\tHide log location in source code\n", APP_NAME); fprintf(stdout, "%s:\t -e 0/1\t\t\tTimestamps relative to first entry seen. Defaults to\n", APP_NAME); fprintf(stdout, "%s:\t\t\t\tabsolute when -r(aw), relative otherwise.\n", APP_NAME); fprintf(stdout, "%s:\t -f precision\t\tSet timestamp precision\n", APP_NAME); fprintf(stdout, "%s:\t -g\t\t\tHide timestamp\n", APP_NAME); fprintf(stdout, "%s:\t -d *.ldc_file \t\tDump ldc_file information\n", APP_NAME); fprintf(stdout, "%s:\t -F filter\t\tUpdate trace filter, format: " "=[, ]\n", APP_NAME); exit(0); } static int snapshot(const char *name) { const char *path = "/sys/kernel/debug/sof"; uint32_t val, addr; char pinname[64], poutname[64], buffer[128]; FILE *in_fd, *out_fd; int i, count; if (!name) { fprintf(stderr, "error: need snapshot name\n"); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(debugfs); i++) { sprintf(pinname, "%s/%s", path, debugfs[i]); sprintf(poutname, "%s.%s.txt", name, debugfs[i]); /* open debugfs for reading */ in_fd = fopen(pinname, "rb"); if (!in_fd) { fprintf(stderr, "error: unable to open %s for reading: %s\n", pinname, strerror(errno)); continue; } /* open outfile for writing */ out_fd = fopen(poutname, "wb"); if (!out_fd) { fprintf(stderr, "error: unable to open %s for writing: %s\n", poutname, strerror(errno)); fclose(in_fd); continue; } fprintf(stdout, "processing %s...\n", pinname); addr = 0; while (1) { count = fread(&val, 1, 4, in_fd); if (count != 4) break; sprintf(buffer, "0x%6.6x: 0x%8.8x\n", addr, val); count = fwrite(buffer, 1, strlen(buffer), out_fd); addr += 4; } fclose(in_fd); fclose(out_fd); } return 0; } static int configure_uart(const char *file, unsigned int baud) { struct termios tio = {}; int ret, fd = open(file, O_RDWR | O_NOCTTY); if (fd < 0) return -errno; cfsetspeed(&tio, 115200); cfmakeraw(&tio); tio.c_iflag |= IGNBRK; tio.c_cflag |= CLOCAL | CREAD | HUPCL; tio.c_cc[VTIME] = 1; tio.c_cc[VMIN] = 1; ret = tcsetattr(fd, TCSANOW, &tio); return ret < 0 ? -errno : fd; } /* Concantenate `config->filter_config` with `input` + `\n` */ static int append_filter_config(struct convert_config *config, const char *input) { char *old_config = config->filter_config; /* filer_config can't be NULL for following steps */ if (!old_config) old_config = log_asprintf("%s", ""); config->filter_config = log_asprintf("%s%s\n", old_config, input); free(old_config); if (!config->filter_config) return -ENOMEM; return 0; } int main(int argc, char *argv[]) { static const char optstring[] = "ho:i:l:ps:c:u:tv:rd:Le:f:gF:n"; struct convert_config config; unsigned int baud = 0; const char *snapshot_file = 0; int opt, ret = 0; config.trace = 0; config.clock = DEFAULT_CLOCK_MHZ; config.in_file = NULL; config.out_file = NULL; config.out_fd = NULL; config.in_fd = NULL; config.ldc_file = NULL; config.ldc_fd = NULL; config.input_std = 0; /* checking fw version is disabled by default */ config.version_file = "/sys/kernel/debug/sof/fw_version"; config.version_fd = NULL; config.version_fw = 1; config.use_colors = 1; config.serial_fd = -EINVAL; config.raw_output = 0; config.dump_ldc = 0; config.hide_location = 0; config.time_precision = 6; config.relative_timestamps = INT_MAX; /* unspecified */ config.filter_config = NULL; while ((opt = getopt(argc, argv, optstring)) != -1) { switch (opt) { case 'o': config.out_file = optarg; break; case 'i': config.in_file = optarg; break; case 't': config.trace = 1; break; case 'c': config.clock = atof(optarg); break; case 's': snapshot_file = optarg; break; case 'l': if (config.ldc_file) { fprintf(stderr, "error: Multiple ldc files\n"); usage(); } config.ldc_file = optarg; break; case 'p': config.input_std = 1; break; case 'u': baud = atoi(optarg); break; case 'r': config.raw_output = 1; break; case 'v': /* enabling checking fw version with ver_file file */ config.version_file = optarg; break; case 'n': config.version_fw = 0; break; case 'L': config.hide_location = 1; break; case 'e': { int i = atoi(optarg); if (i < 0 || 1 < i) { fprintf(stderr, "%s: invalid option: -e %s\n", APP_NAME, optarg); return -EINVAL; } config.relative_timestamps = i; break; } case 'f': config.time_precision = atoi(optarg); if (config.time_precision < 0) { usage(); return -EINVAL; } break; case 'g': config.time_precision = -1; break; case 'd': if (config.ldc_file) { fprintf(stderr, "error: Multiple ldc files\n"); usage(); } config.dump_ldc = 1; config.ldc_file = optarg; break; case 'F': ret = append_filter_config(&config, optarg); if (ret < 0) return ret; break; case 'h': default: /* '?' */ usage(); } } if (argc != optind) { fprintf(stderr, "error: Unused parameter '%s'\n", argv[optind]); usage(); } if (snapshot_file) return baud ? EINVAL : -snapshot(snapshot_file); if (!config.ldc_file) { fprintf(stderr, "error: Missing ldc file\n"); usage(); } config.ldc_fd = fopen(config.ldc_file, "rb"); if (!config.ldc_fd) { ret = errno; fprintf(stderr, "error: Unable to open ldc file %s: %s\n", config.ldc_file, strerror(ret)); goto out; } if (config.version_fw) { config.version_fd = fopen(config.version_file, "rb"); if (!config.version_fd && !config.dump_ldc) { ret = errno; fprintf(stderr, "error: Unable to open version file %s: %s\n", config.version_file, strerror(ret)); goto out; } } if (config.out_file) { config.out_fd = fopen(config.out_file, "w"); if (!config.out_fd) { ret = errno; fprintf(stderr, "error: Unable to open out file %s: %s\n", config.out_file, strerror(ret)); goto out; } } else { config.out_fd = stdout; } /* trace requested ? */ if (config.trace) config.in_file = "/sys/kernel/debug/sof/trace"; /* Default value when -e is not specified */ if (config.relative_timestamps == INT_MAX) config.relative_timestamps = config.raw_output ? 0 : 1; /* default option with no infile is to dump errors/debug data */ if (!config.in_file && !config.dump_ldc) config.in_file = "/sys/kernel/debug/sof/etrace"; if (config.input_std) { config.in_fd = stdin; } else if (baud) { config.serial_fd = configure_uart(config.in_file, baud); if (config.serial_fd < 0) { ret = -config.serial_fd; goto out; } } else if (config.in_file) { config.in_fd = fopen(config.in_file, "rb"); if (!config.in_fd) { ret = errno; fprintf(stderr, "error: Unable to open in file %s: %s\n", config.in_file, strerror(ret)); goto out; } } if (isatty(fileno(config.out_fd)) != 1) config.use_colors = 0; ret = -convert(&config); out: /* free memory */ if (config.filter_config) free(config.filter_config); /* close files */ if (config.out_fd) fclose(config.out_fd); if (config.in_fd) fclose(config.in_fd); if (config.ldc_fd) fclose(config.ldc_fd); if (config.version_fd) fclose(config.version_fd); return ret; }