A option 'list-all' is to display both current config variables and all possible config variables with default values. The syntax examples are like below
perf config [options] display all perf config with default values. # perf config -a | --list-all Signed-off-by: Taeung Song <treeze.tae...@gmail.com> --- tools/perf/Documentation/perf-config.txt | 6 + tools/perf/builtin-config.c | 490 ++++++++++++++++++++++++++++++- tools/perf/util/cache.h | 15 + 3 files changed, 510 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index b2abb16..d95baad 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -9,6 +9,8 @@ SYNOPSIS -------- [verse] 'perf config' [<file-option>] -l | --list +or +'perf config' [<file-option>] -a | --list-all DESCRIPTION ----------- @@ -29,6 +31,10 @@ OPTIONS For writing and reading options: write to system-wide '$(sysconfdir)/perfconfig' or read it. +-a:: +--list-all:: + Show current and all possible config variables with default values. + CONFIGURATION FILE ------------------ diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c index df70b90..ac13aaf 100644 --- a/tools/perf/builtin-config.c +++ b/tools/perf/builtin-config.c @@ -22,7 +22,8 @@ static const char * const config_usage[] = { }; enum actions { - ACTION_LIST = 1 + ACTION_LIST = 1, + ACTION_LIST_ALL } actions; static const struct option config_options[] = { @@ -32,9 +33,362 @@ static const struct option config_options[] = { OPT_GROUP("Action"), OPT_SET_UINT('l', "list", &actions, "show current config variables", ACTION_LIST), + OPT_SET_UINT('a', "list-all", &actions, + "show current and all possible config variables with default values", + ACTION_LIST_ALL), OPT_END() }; +/* section names */ +#define COLORS "colors" +#define TUI "tui" +#define BUILDID "buildid" +#define ANNOTATE "annotate" +#define GTK "gtk" +#define PAGER "pager" +#define HELP "help" +#define HIST "hist" +#define UI "ui" +#define CALL_GRAPH "call-graph" +#define REPORT "report" +#define TOP "top" +#define MAN "man" +#define KMEM "kmem" + +/* config variable types */ +#define TYPE_INT "int" +#define TYPE_LONG "long" +#define TYPE_DIRNAME "dirname" +#define TYPE_BOOL "bool" +#define TYPE_ON_OFF "on_off" + +static struct default_configset { + const char *section_name; + const char *name, *value, *type; + +} default_configsets[] = { + { + .section_name = COLORS, + .name = "top", + .value = "red, default", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "medium", + .value = "green, default", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "normal", + .value = "lightgray, default", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "selected", + .value = "white, lightgray", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "code", + .value = "blue, default", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "addr", + .value = "magenta, default", + .type = NULL, + }, + { + .section_name = COLORS, + .name = "root", + .value = "white, blue", + .type = NULL, + }, + { + .section_name = TUI, + .name = "report", + .value = "on", + .type = TYPE_ON_OFF, + }, + { + .section_name = TUI, + .name = "annotate", + .value = "on", + .type = TYPE_ON_OFF, + }, + { + .section_name = TUI, + .name = "top", + .value = "on", + .type = TYPE_ON_OFF, + }, + { + .section_name = BUILDID, + .name = "dir", + .value = "~/.debug", + .type = TYPE_DIRNAME, + }, + { + .section_name = ANNOTATE, + .name = "hide_src_code", + .value = "false", + .type = TYPE_BOOL, + }, + { + .section_name = ANNOTATE, + .name = "use_offset", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = ANNOTATE, + .name = "jump_arrows", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = ANNOTATE, + .name = "show_nr_jumps", + .value = "false", + .type = TYPE_BOOL, + }, + { + .section_name = GTK, + .name = "annotate", + .value = "off", + .type = TYPE_ON_OFF, + }, + { + .section_name = GTK, + .name = "report", + .value = "off", + .type = TYPE_ON_OFF, + }, + { + .section_name = GTK, + .name = "top", + .value = "off", + .type = TYPE_ON_OFF, + }, + { + .section_name = PAGER, + .name = "cmd", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = PAGER, + .name = "report", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = PAGER, + .name = "annotate", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = PAGER, + .name = "record", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = PAGER, + .name = "top", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = PAGER, + .name = "diff", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = HELP, + .name = "format", + .value = "man", + .type = NULL, + }, + { + .section_name = HELP, + .name = "autocorrect", + .value = "0", + .type = NULL, + }, + { + .section_name = HIST, + .name = "percentage", + .value = "absolute", + .type = NULL, + }, + { + .section_name = UI, + .name = "show-headers", + .value = "true", + .type = TYPE_BOOL, + }, + { + .section_name = CALL_GRAPH, + .name = "record-mode", + .value = "fp", + }, + { + .section_name = CALL_GRAPH, + .name = "dump-size", + .value = "8192", + .type = TYPE_INT, + }, + { + .section_name = CALL_GRAPH, + .name = "print-type", + .value = "fractal", + .type = NULL, + }, + { + .section_name = CALL_GRAPH, + .name = "order", + .value = "caller", + .type = NULL, + }, + { + .section_name = CALL_GRAPH, + .name = "sort-key", + .value = "function", + .type = NULL, + }, + { + .section_name = CALL_GRAPH, + .name = "threshold", + .value = "0.5", + .type = TYPE_LONG, + }, + { + .section_name = CALL_GRAPH, + .name = "print-limit", + .value = "0", + .type = TYPE_INT, + }, + { + .section_name = REPORT, + .name = "children", + .value = "false", + .type = TYPE_BOOL, + }, + { + .section_name = REPORT, + .name = "percent-limit", + .value = "0", + .type = TYPE_INT, + }, + { + .section_name = REPORT, + .name = "queue-size", + .value = "0", + .type = TYPE_INT, + }, + { + .section_name = TOP, + .name = "children", + .value = "false", + .type = TYPE_BOOL, + }, + { + .section_name = MAN, + .name = "viewer", + .value = "man", + .type = NULL, + }, + { + .section_name = KMEM, + .name = "default", + .value = "slab", + .type = NULL, + }, + { + .section_name = NULL, + .name = NULL, + .value = NULL, + .type = NULL, + }, +}; + +static struct config_section *find_section(const char *section_name) +{ + struct config_section *section_node; + + list_for_each_entry(section_node, §ions, list) + if (!strcmp(section_node->name, section_name)) + return section_node; + + return NULL; +} + +static struct config_element *find_element(const char *name, + struct config_section *section_node) +{ + struct config_element *element_node; + + list_for_each_entry(element_node, §ion_node->element_head, list) + if (!strcmp(element_node->name, name)) + return element_node; + + return NULL; +} + +static struct config_section *init_section(const char *section_name) +{ + struct config_section *section_node; + + section_node = zalloc(sizeof(*section_node)); + if (!section_node) + return NULL; + + INIT_LIST_HEAD(§ion_node->element_head); + section_node->name = strdup(section_name); + if (!section_node->name) { + pr_err("%s: strdup failed\n", __func__); + free(section_node); + return NULL; + } + + return section_node; +} + +static int add_element(struct list_head *head, + const char *name, const char *value) +{ + struct config_element *element_node; + + element_node = zalloc(sizeof(*element_node)); + if (!element_node) + return -1; + + element_node->name = strdup(name); + if (!element_node->name) { + pr_err("%s: strdup failed\n", __func__); + goto out_free; + } + if (value) + element_node->value = (char *)value; + else + element_node->value = NULL; + + list_add_tail(&element_node->list, head); + return 0; + +out_free: + free(element_node); + return -1; +} + static int show_config(const char *key, const char *value, void *cb __maybe_unused) { @@ -46,6 +400,132 @@ static int show_config(const char *key, const char *value, return 0; } +static void find_config(struct config_section **section_node, + struct config_element **element_node, + const char *section_name, const char *name) +{ + *section_node = find_section(section_name); + + if (*section_node != NULL) + *element_node = find_element(name, *section_node); + else + *element_node = NULL; +} + +static char *normalize_value(const char *section_name, const char *name, const char *value) +{ + int i, ret = 0; + char key[BUFSIZ]; + char *normalized; + + scnprintf(key, sizeof(key), "%s.%s", section_name, name); + for (i = 0; default_configsets[i].section_name != NULL; i++) { + struct default_configset *config = &default_configsets[i]; + + if (!strcmp(config->section_name, section_name) + && !strcmp(config->name, name)) { + if (!config->type) + ret = asprintf(&normalized, "%s", value); + else if (!strcmp(config->type, TYPE_BOOL)) + ret = asprintf(&normalized, "%s", + perf_config_bool(key, value) ? "true" : "false"); + else if (!strcmp(config->type, TYPE_ON_OFF)) + ret = asprintf(&normalized, "%s", + perf_config_bool(key, value) ? "on" : "off"); + else if (!strcmp(config->type, TYPE_INT)) + ret = asprintf(&normalized, "%d", + perf_config_int(key, value)); + else if (!strcmp(config->type, TYPE_LONG)) + ret = asprintf(&normalized, "%"PRId64, + perf_config_u64(key, value)); + else if (!strcmp(config->type, TYPE_DIRNAME)) + ret = asprintf(&normalized, "%s", + perf_config_dirname(key, value)); + if (ret < 0) + return NULL; + + return normalized; + } + } + + normalized = strdup(value); + if (!normalized) { + pr_err("%s: strdup failed\n", __func__); + return NULL; + } + + return normalized; +} + +static int collect_current_config(const char *var, const char *value, + void *cb __maybe_unused) +{ + struct config_section *section_node; + char *key = strdup(var); + char *section_name, *name; + + if (!key) { + pr_err("%s: strdup failed\n", __func__); + return -1; + } + + section_name = strsep(&key, "."); + name = strsep(&key, "."); + free(key); + if (name == NULL) + return -1; + + section_node = find_section(section_name); + if (!section_node) { + section_node = init_section(section_name); + if (!section_node) + return -1; + list_add_tail(§ion_node->list, §ions); + } + + return add_element(§ion_node->element_head, name, + normalize_value(section_name, name, value)); +} + +static int show_all_config(void) +{ + int i; + bool has_config; + struct config_section *section_node; + struct config_element *element_node; + + for (i = 0; default_configsets[i].section_name != NULL; i++) { + find_config(§ion_node, &element_node, + default_configsets[i].section_name, default_configsets[i].name); + + if (!element_node) + printf("%s.%s=%s\n", default_configsets[i].section_name, + default_configsets[i].name, default_configsets[i].value); + else + printf("%s.%s=%s\n", section_node->name, + element_node->name, element_node->value); + } + + /* Print config variables the default configsets haven't */ + list_for_each_entry(section_node, §ions, list) { + list_for_each_entry(element_node, §ion_node->element_head, list) { + has_config = false; + for (i = 0; default_configsets[i].section_name != NULL; i++) { + if (!strcmp(default_configsets[i].section_name, section_node->name) + && !strcmp(default_configsets[i].name, element_node->name)) { + has_config = true; + break; + } + } + if (!has_config) + printf("%s.%s=%s\n", section_node->name, + element_node->name, element_node->value); + } + } + + return 0; +} + int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) { int ret = 0; @@ -62,7 +542,15 @@ int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) access(config_file_name, R_OK) == -1)) config_file_name = perf_etc_perfconfig(); + INIT_LIST_HEAD(§ions); + perf_config_from_file(collect_current_config, config_file_name, NULL); + switch (actions) { + case ACTION_LIST_ALL: + if (argc == 0) { + ret = show_all_config(); + break; + } case ACTION_LIST: default: if (argc) { diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index bad3e4e..f49a5e2 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -1,6 +1,7 @@ #ifndef __PERF_CACHE_H #define __PERF_CACHE_H +#include <linux/list.h> #include <stdbool.h> #include "util.h" #include "strbuf.h" @@ -19,6 +20,20 @@ #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" +struct config_element { + char *name; + char *value; + struct list_head list; +}; + +struct config_section { + char *name; + struct list_head element_head; + struct list_head list; +}; + +struct list_head sections; + typedef int (*config_fn_t)(const char *, const char *, void *); extern int perf_default_config(const char *, const char *, void *); extern int perf_config(config_fn_t fn, void *); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/