mirror of https://github.com/thesofproject/sof.git
logger: Parse runtime filter levels
Parse arguments given in -F flag, in form where each -F argument is given as separate line. Format of single -F entry is `<log_level>=<component_list>`. <log_level> must corresponds with .name field from log_level_dict. List of component are build from elements separated with `,` and single element may have one of the following forms: "<name> <pipe_id>.<comp_id>" - single component instance "<name> <pipe_id>.*" - components with given name on piepeline with pipe_id "<name>*" - each component instance with given name or global component "<name>" - as above "* <pipe_id>.<comp_id>" - component on selected pipe_id with given comp_id "* <pipe_id>.*" - each component on pipeline with pipe_id "*" - each component in firmware <name> must correspond values of uuid entries from ldc file. Signed-off-by: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
This commit is contained in:
parent
97a8c685f8
commit
b1fc922079
|
@ -3,6 +3,7 @@
|
|||
add_executable(sof-logger
|
||||
logger.c
|
||||
convert.c
|
||||
filter.c
|
||||
misc.c
|
||||
)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <user/abi_dbg.h>
|
||||
#include <user/trace.h>
|
||||
#include "convert.h"
|
||||
#include "filter.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define CEIL(a, b) ((a+b-1)/b)
|
||||
|
@ -763,5 +764,15 @@ int convert(struct convert_config *config)
|
|||
if (config->dump_ldc)
|
||||
return dump_ldc_info(config, &snd);
|
||||
|
||||
if (config->filter_config) {
|
||||
ret = filter_update_firmware(config->uids_dict,
|
||||
config->filter_config);
|
||||
if (ret) {
|
||||
log_err(config->out_fd,
|
||||
"failed to apply trace filter, %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return logger_read(config, &snd);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright(c) 2020 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <ipc/trace.h>
|
||||
#include <smex/ldc.h>
|
||||
#include <sof/lib/uuid.h>
|
||||
#include <sof/list.h>
|
||||
#include <user/trace.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "convert.h"
|
||||
#include "filter.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define COMPONENTS_SEPARATOR ','
|
||||
#define COMPONENT_NAME_SCAN_STRING_LENGTH 32
|
||||
|
||||
/** map between log level given by user and enum value */
|
||||
static const struct {
|
||||
const char name[16];
|
||||
int32_t log_level;
|
||||
} log_level_dict[] = {
|
||||
{"verbose", LOG_LEVEL_VERBOSE},
|
||||
{"debug", LOG_LEVEL_DEBUG},
|
||||
{"info", LOG_LEVEL_INFO},
|
||||
{"warning", LOG_LEVEL_WARNING},
|
||||
{"error", LOG_LEVEL_ERROR},
|
||||
{"critical", LOG_LEVEL_CRITICAL},
|
||||
{"v", LOG_LEVEL_VERBOSE},
|
||||
{"d", LOG_LEVEL_DEBUG},
|
||||
{"i", LOG_LEVEL_INFO},
|
||||
{"w", LOG_LEVEL_WARNING},
|
||||
{"e", LOG_LEVEL_ERROR},
|
||||
{"c", LOG_LEVEL_CRITICAL},
|
||||
};
|
||||
|
||||
struct filter_element {
|
||||
struct list_item list;
|
||||
int32_t uuid_id; /**< type id, or -1 when not important */
|
||||
int32_t comp_id; /**< component id or -1 when not important */
|
||||
int32_t pipe_id; /**< pipeline id or -1 when not important */
|
||||
int32_t log_level; /**< new log level value */
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for uuid entry with given component name in given uids dictionary
|
||||
* @param uids_dict dictionary to search in
|
||||
* @param name of uuid entry
|
||||
* @return pointer to sof_uuid_entry with given name
|
||||
*/
|
||||
static struct sof_uuid_entry *
|
||||
get_uuid_by_name(const struct snd_sof_uids_header *uids_dict,
|
||||
const char *name)
|
||||
{
|
||||
uintptr_t beg = (uintptr_t)uids_dict + uids_dict->data_offset;
|
||||
uintptr_t end = beg + uids_dict->data_length;
|
||||
struct sof_uuid_entry *ptr = (struct sof_uuid_entry *)beg;
|
||||
|
||||
while ((uintptr_t)ptr < end) {
|
||||
if (strcmp(name, ptr->name) == 0)
|
||||
return ptr;
|
||||
++ptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse log level name (from log_level_dict) to enum value.
|
||||
* Take care about possible whitespace at the begin.
|
||||
* @param value_start pointer to the begin of range to search
|
||||
* @return enum value for given log level, or -1 for invalid value
|
||||
*/
|
||||
static int filter_parse_log_level(const char *value_start)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(log_level_dict); ++i) {
|
||||
if (strstr(log_level_dict[i].name, value_start))
|
||||
return log_level_dict[i].log_level;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *filter_parse_component_name(const struct snd_sof_uids_header *uids_dict,
|
||||
char *input_str,
|
||||
struct filter_element *out)
|
||||
{
|
||||
static char scan_format_string[COMPONENT_NAME_SCAN_STRING_LENGTH] = "";
|
||||
char comp_name[UUID_NAME_MAX_LEN];
|
||||
struct sof_uuid_entry *uuid_entry;
|
||||
int ret;
|
||||
|
||||
/* if component name is not specified, stay with default out->uuid_id value */
|
||||
if (input_str[0] == '*')
|
||||
return &input_str[1];
|
||||
|
||||
/*
|
||||
* Take care about buffer overflows when dealing with input from
|
||||
* user, so scan no more than UUID_NAME_MAX_LEN bytes to
|
||||
* `comp_name` variable. Only once initialise scan_format_string.
|
||||
*/
|
||||
if (strlen(scan_format_string) == 0) {
|
||||
ret = snprintf(scan_format_string, sizeof(scan_format_string),
|
||||
"%%%d[^0-9* ]s", UUID_NAME_MAX_LEN);
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
}
|
||||
ret = sscanf(input_str, scan_format_string, comp_name);
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
|
||||
/* find component uuid key */
|
||||
uuid_entry = get_uuid_by_name(uids_dict, comp_name);
|
||||
if (!uuid_entry) {
|
||||
log_err(NULL, "unknown component name `%s`\n", comp_name);
|
||||
return NULL;
|
||||
}
|
||||
out->uuid_id = get_uuid_key(uids_dict, uuid_entry);
|
||||
return strstr(input_str, comp_name) + strlen(comp_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse component definition from input_str.
|
||||
*
|
||||
* Possible input_str formats:
|
||||
* `name pipe_id.comp_id`
|
||||
* `name pipe_id.*`
|
||||
* `name *`
|
||||
* `name`
|
||||
* `* pipe_id.comp_id`
|
||||
* `* pipe_id.*`
|
||||
* `*`
|
||||
* Whitespace is possible at the begin, end and after `name`.
|
||||
* `name` must refer to values from given UUID dictionary,
|
||||
* (so name comes from DECLARE_SOF_UUID macro usage)
|
||||
|
||||
* @param uids_dict dictionary with list of possible `name` values
|
||||
* @param input_str formatted component definition
|
||||
* @param out element where component definition should be saved
|
||||
*/
|
||||
static int filter_parse_component(const struct snd_sof_uids_header *uids_dict,
|
||||
char *input_str,
|
||||
struct filter_element *out)
|
||||
{
|
||||
char *instance_info;
|
||||
int ret;
|
||||
|
||||
/* trim whitespaces, to easily check first and last char */
|
||||
input_str = trim(input_str);
|
||||
|
||||
/* assign default values */
|
||||
out->uuid_id = 0;
|
||||
out->pipe_id = -1;
|
||||
out->comp_id = -1;
|
||||
|
||||
/* parse component name and store pointer after component name, pointer to instance info */
|
||||
instance_info = filter_parse_component_name(uids_dict, input_str, out);
|
||||
if (!instance_info) {
|
||||
log_err(NULL, "component name parsing `%s`\n",
|
||||
input_str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* if instance is not specified then stop parsing */
|
||||
instance_info = ltrim(instance_info);
|
||||
if (instance_info[0] == '\0' ||
|
||||
(instance_info[0] == '*' && instance_info[1] == '\0')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now parse last part: `number.x` where x is a number or `*` */
|
||||
ret = sscanf(instance_info, "%d.%d", &out->pipe_id, &out->comp_id);
|
||||
if (ret == 2)
|
||||
return 0;
|
||||
else if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* pipeline id parsed but component id is not a number */
|
||||
if (instance_info[strlen(instance_info) - 1] == '*')
|
||||
return 0;
|
||||
log_err(NULL, "Use * to specify each component on particular pipeline\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert argument from -F flag to sof_ipc_dma_trace_filter_elem struct values.
|
||||
*
|
||||
* Possible log_level - see filter_parse_log_level() documentation
|
||||
* Possible component - list of components separated by `COMPONENTS_SEPARATOR`,
|
||||
* for single component definition description look at
|
||||
* filter_parse_component() documentation
|
||||
*
|
||||
* Examples:
|
||||
* `debug="pipe1"` - set debug log level for components from pipeline1
|
||||
* `d="pipe1, dai2.3"` - as above, but also for dai2.3
|
||||
* `error="FIR*"` - for each FIR component set log level to error
|
||||
*
|
||||
* @param dictionary uuid dictionary from ldc file
|
||||
* @param input_str log level settings in format `log_level=component`
|
||||
* @param out_list output list with filter_element elements
|
||||
*/
|
||||
static int filter_parse_entry(const struct snd_sof_uids_header *uids_dict,
|
||||
char *input_str, struct list_item *out_list)
|
||||
{
|
||||
struct filter_element *filter;
|
||||
char *comp_fmt_end;
|
||||
int32_t log_level;
|
||||
char *comp_fmt;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* split string on '=' char, left part describes log level,
|
||||
* the right one is for component description.
|
||||
*/
|
||||
comp_fmt = strchr(input_str, '=');
|
||||
if (!comp_fmt) {
|
||||
log_err(NULL, "unable to find `=` in `%s`\n", input_str);
|
||||
return -EINVAL;
|
||||
}
|
||||
*comp_fmt = 0;
|
||||
++comp_fmt;
|
||||
|
||||
/* find correct log level in given conf string - string before `=` */
|
||||
log_level = filter_parse_log_level(input_str);
|
||||
if (log_level < 0) {
|
||||
log_err(NULL, "unable to parse log level from `%s`\n",
|
||||
input_str);
|
||||
return log_level;
|
||||
}
|
||||
|
||||
/*
|
||||
* now parse list of components name and optional instance identifier,
|
||||
* split string on `COMPONENTS_SEPARATOR`
|
||||
*/
|
||||
while (comp_fmt) {
|
||||
filter = malloc(sizeof(struct filter_element));
|
||||
if (!filter) {
|
||||
log_err(NULL, "unable to malloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
comp_fmt_end = strchr(comp_fmt, COMPONENTS_SEPARATOR);
|
||||
if (comp_fmt_end)
|
||||
*comp_fmt_end = '\0';
|
||||
ret = filter_parse_component(uids_dict, comp_fmt, filter);
|
||||
if (ret < 0) {
|
||||
log_err(NULL, "unable to parse component from `%s`\n",
|
||||
comp_fmt);
|
||||
free(filter);
|
||||
return ret;
|
||||
}
|
||||
filter->log_level = log_level;
|
||||
|
||||
list_item_append(&filter->list, out_list);
|
||||
comp_fmt = comp_fmt_end ? comp_fmt_end + 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse `input_str` content.
|
||||
*
|
||||
* `input_str` contain single filter definition element per line.
|
||||
* Each line is parsed by `filter_parse_entry`, and saved in list.
|
||||
*
|
||||
* @param dictionary uuid dictionary from ldc file
|
||||
* @param format log level settings in format `log_level=component`
|
||||
*/
|
||||
int filter_update_firmware(const struct snd_sof_uids_header *uids_dict,
|
||||
char *input_str)
|
||||
{
|
||||
struct filter_element *filter;
|
||||
struct list_item filter_list;
|
||||
struct list_item *list_elem;
|
||||
struct list_item *list_temp;
|
||||
char *line_end;
|
||||
int ret = 0;
|
||||
|
||||
list_init(&filter_list);
|
||||
|
||||
/* parse `input_str` line by line */
|
||||
line_end = strchr(input_str, '\n');
|
||||
while (line_end) {
|
||||
line_end[0] = '\0';
|
||||
ret = filter_parse_entry(uids_dict, input_str, &filter_list);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
input_str = line_end + 1;
|
||||
line_end = strchr(input_str, '\n');
|
||||
}
|
||||
|
||||
err:
|
||||
/* free each component from parsed element list */
|
||||
list_for_item_safe(list_elem, list_temp, &filter_list) {
|
||||
filter = container_of(list_elem, struct filter_element,
|
||||
list);
|
||||
free(filter);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright(c) 2020 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef __LOGGER_FILTER_H__
|
||||
#define __LOGGER_FILTER_H__
|
||||
|
||||
int filter_update_firmware(const struct snd_sof_uids_header *uids_dict,
|
||||
char *input_str);
|
||||
|
||||
#endif /* __LOGGER_FILTER_H__ */
|
Loading…
Reference in New Issue